/*
 * JSF Toolkit Component Framework
 * Copyright (C) 2007 Noah Sloan <iamnoah A-T gmail D0T com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 */
package com.jsftoolkit.gen;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.faces.component.EditableValueHolder;

import org.xml.sax.SAXException;

import com.jsftoolkit.base.ResourceInfo;
import com.jsftoolkit.base.renderer.DecodeEvent;
import com.jsftoolkit.base.renderer.HtmlRenderer;
import com.jsftoolkit.base.renderer.RenderEventsCollector;
import com.jsftoolkit.base.renderer.VarAttribEvent;
import com.jsftoolkit.gen.info.ComponentInfo;
import com.jsftoolkit.gen.info.RendererInfo;
import com.jsftoolkit.gen.writer.RendererWriter;
import com.jsftoolkit.gen.writer.ClassWriter.TypeParameter;
import com.jsftoolkit.utils.Utils;

/**
 * Reads in an XHTML template and generates the component, renderer and tag
 * handler. The component and tag handler are generated using
 * {@link ComponentGeneratorImpl}, but this class adds additional metadata from
 * the parsed template.
 * 
 * @author noah
 */
public class RendererGeneratorImpl extends
		AbstractComponentClassGenerator<RendererWriter> {

	private static final String DEFAULT_TEMPLATE_ENC = "UTF-8";

	/**
	 * 
	 * @param info
	 *            the {@link ComponentInfo}
	 * @param resource
	 *            the classpath resource that is the template
	 * @param encoding
	 *            the encoding of the template
	 * @param out
	 */
	@SuppressWarnings("unchecked")
	public void generate(ComponentInfo info, RendererWriter writer) {
		RendererInfo renderInfo = info.getRenderer();

		if(!renderInfo.getIncludes().isEmpty()) {
			// only import Type if we're going to use it.
			renderInfo.addImport(ResourceInfo.Type.class);
		}
		
		printToBody(writer, renderInfo, HtmlRenderer.class);

		Set<String> resources = new LinkedHashSet<String>();

		// create a constant for each resource required
		for (ResourceInfo ii : info.getRenderer().getIncludes()) {
			String id = ii.getId();
			String name = id.substring(id.lastIndexOf('.') + 1);
			writer.printConstant(ResourceInfo.class, name, ii.getCodeString());
			resources.add(name);
		}

		// prepare the template information
		String encoding = Utils.toStringConstant(Utils.getValue(renderInfo
				.getTemplateEncoding(), DEFAULT_TEMPLATE_ENC));
		String templates = Utils.toStringConstantArray((Iterable<String>)renderInfo
				.getTemplates());

		// create a set of the constants
		writer.printConstant(new TypeParameter<Set<ResourceInfo>>() {
		}.getClass(), "RESOURCES", String.format("Utils.asSet(%s)", Utils.join(
				resources.iterator(), ",")));

		// write the constructor
		writer.startConstructor(Modifier.PUBLIC, null, IOException.class,
				SAXException.class);
		writer.indent().printf(
				"super(HtmlRenderer.combineTemplates(%s,new String[]%s), RESOURCES);",
				encoding, templates);

		// add decode information, if it was provided
		String decodeParam = info.getRenderer().getDecodeParam();
		if ((EditableValueHolder.class.isAssignableFrom(info.getSuperClass()) || info
				.getInterfaces().contains(EditableValueHolder.class))
				&& !Utils.isEmpty(decodeParam)) {
			writer.print('\n').indent();
			writeDecode(writer, decodeParam);
		}
		writer.endBlock(); // end constructor

		writer.printRendersChildren(true);

		writer.close();
	}

	protected void writeDecode(RendererWriter writer, String decodeParam) {
		VarAttribEvent vae = RenderEventsCollector
				.convertToVarAttribute(new DecodeEvent(decodeParam));
		String format = Utils.toStringConstant(vae == null ? decodeParam : vae
				.getPattern());
		String props = Utils.toStringConstantArray(vae.getProperties()
				.toArray());
		String defaults = Utils.toStringConstantArray(vae.getDefaultValues()
				.toArray());
		writer.printf("setDecodeInfo(%s, new String[]%s, new String[]%s);",
				format, props, defaults);
	}

}
